package com.zj.service.impl;

import com.zj.consts.GoodsConst;
import com.zj.pojo.dto.GoodsDTO;
import com.zj.pojo.model.Goods;
import com.zj.pojo.vo.AggGoodsVO;
import com.zj.pojo.vo.FinalResultVO;
import com.zj.pojo.vo.GoodsVO;
import com.zj.service.GoodsService;
import com.zj.utils.BeanCopier;
import com.zj.utils.HtmlParseUtils;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedDoubleTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms.Bucket;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;

/**
 * @author 赵赳
 * @since 2021/12/22 9:39
 */
@Slf4j
@Service
public class GoodsServiceImpl implements GoodsService {

  private final ElasticsearchRestTemplate restTemplate;


  public GoodsServiceImpl(
      ElasticsearchRestTemplate restTemplate
  ) {
    this.restTemplate = restTemplate;
  }


  @Override
  public boolean parseGoodsToEs(String keys) throws Exception {
    List<Goods> goodsData = HtmlParseUtils.parseJDGoodsData(keys);
    var save = restTemplate.save(goodsData);
    return save.iterator().hasNext();
  }

  @Override
  public FinalResultVO<Map<String, Object>> getGoods(GoodsDTO dto) {
    var queryBuilder = new NativeSearchQueryBuilder()
        .withQuery(
            QueryBuilders.boolQuery()
                .must(QueryBuilders.matchQuery(GoodsConst.TITLE, dto.getKeyword()))
        )  // 匹配查询
        //.withQuery(QueryBuilders.matchQuery(GoodsConst.TITLE,dto.getKeyword()))
        .withSort(SortBuilders.fieldSort(GoodsConst.PRICE).order(SortOrder.ASC)) // 排序
        .withPageable(PageRequest.of(dto.getPageNo(), dto.getPageSize())) // 分页
        .withHighlightFields(new HighlightBuilder  // 高亮查询
            .Field(GoodsConst.TITLE)
            .preTags(GoodsConst.PRE_TAGS)
            .postTags(GoodsConst.POST_TAGS)
        )
        .addAggregation( // 聚合查询
            AggregationBuilders.terms("agg_price").field("price")
                .subAggregation(AggregationBuilders.terms("avg_price").field("price"))
        );
    // 价格过滤
    if (Objects.nonNull(dto.getMaxPrice()) && Objects.nonNull(dto.getMinPrice())) {
      queryBuilder.withFilter(
          QueryBuilders.rangeQuery(GoodsConst.PRICE)
              .gte(dto.getMinPrice())
              .lte(dto.getMaxPrice())
      );
    }

    Map<String, Object> map = new LinkedHashMap<>();
    List<AggGoodsVO> aggGoodsVOS = new ArrayList<>();

    var search = restTemplate.search(queryBuilder.build(), Goods.class);
    /* 聚合数据 */
    var aggregations = search.getAggregations();
    assert aggregations != null;
    ParsedDoubleTerms price = aggregations.get("agg_price");
    /* 获取桶bucket */
    List<? extends Bucket> buckets = price.getBuckets();
    for (Bucket bucket : buckets) {
      // 获取桶中的key，即price
      var aggPrice = bucket.getKeyAsString();
      // 获取桶中的文档数量
      var priceCount = bucket.getDocCount();
      // 获取子聚合结果
      ParsedDoubleTerms avg = bucket.getAggregations().get("avg_price");
      var avgPrice = avg.getBuckets().get(0).getKeyAsString();
      aggGoodsVOS.add(new AggGoodsVO()
          .setAggPrice(aggPrice)
          .setCount(priceCount)
          .setAvgPrice(avgPrice)
      );
    }
    // 查询数据
    var goodsVOList = search.stream().map(goodsSearchHit -> {
          var goodsVO = BeanCopier.copy(goodsSearchHit.getContent(), GoodsVO.class);
          var highlightField = goodsSearchHit.getHighlightField(GoodsConst.TITLE);
          highlightField.forEach(goodsVO::setTitle);
          return goodsVO;
        }
    ).collect(Collectors.toList());
    // 封装数据
    map.put("total", search.getTotalHits());
    map.put("data", goodsVOList);
    map.put("agg", aggGoodsVOS);

    return FinalResultVO.ok(map);
  }


}
